﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MSpecLauncher.Model
{
    public enum ESpecStatus
    {
        Unknown = 0,
        Passed,
        Ignored,
        Failed,
        NotImplemented
    }

    public class SpecError
    {
        public string Type { get; private set; }
        public string Message { get; private set; }
        public string StackTrace { get; private set; }

        public SpecError(string type, string message, string stackTrace)
        {
            this.Type = type;
            this.Message = message;
            this.StackTrace = stackTrace;
        }
    }

    public class Spec
    {
        public string Name { get; private set; }
        public int DurationInMillis { get; private set; }
        public ESpecStatus Status { get; private set; }
        public string Output { get; private set; }
        public IEnumerable<SpecError> SpecErrors { get; private set; }

        public Spec(string name, int durationInMillis, ESpecStatus status, string output, IEnumerable<SpecError> specErrors)
        {
            this.Name = name;
            this.DurationInMillis = durationInMillis;
            this.Status = status;
            this.Output = output;
            this.SpecErrors = specErrors;
        }
    }

    public class SpecContext
    {
        public string Name { get; private set; }
        public int DurationInMillis { get; private set; }
        public IEnumerable<Spec> Specs { get; private set; }

        public SpecContext(string name, int durationInMillis, IEnumerable<Spec> specs)
        {
            this.Name = name;
            this.DurationInMillis = durationInMillis;
            this.Specs = specs;
        }
    }

    public class SpecSubject
    {
        public string Name { get; private set; }
        public IEnumerable<SpecContext> Contexts { get; private set; }

        public SpecSubject(string name, IEnumerable<SpecContext> contexts)
        {
            this.Name = name;
            this.Contexts = contexts;
        }

        public int DurationInMillis
        {
            get
            {
                int duration = 0;
                foreach (SpecContext context in this.Contexts)
                {
                    foreach (Spec spec in context.Specs)
                    {
                        duration += spec.DurationInMillis;
                    }
                }

                return duration;
            }
        }

        public bool AllPerfect
        {
            get
            {
                foreach (SpecContext context in this.Contexts)
                {
                    foreach (Spec spec in context.Specs)
                    {
                        if (spec.Status != ESpecStatus.Passed) return false;
                    }
                }

                return true;
            }
        }

        public int ContextsCount { get { return this.Contexts.Count(); } }

        public void GetSpecsCount(out int specCount, out int passedCount)
        {
            specCount = 0;
            passedCount = 0;
            foreach (SpecContext context in this.Contexts)
            {
                foreach (Spec spec in context.Specs)
                {
                    specCount++;
                    if (spec.Status == ESpecStatus.Passed) passedCount++;
                }
            }
        }
    }

    public class AssemblySpecs
    {
        public string Name { get; private set; }
        public string Location { get; private set; }
        public IEnumerable<SpecSubject> Subjects { get; private set; }

        public AssemblySpecs(string name, string location, IEnumerable<SpecSubject> subjects)
        {
            this.Name = name;
            this.Location = location;
            this.Subjects = subjects;
        }

        public int DurationInMillis
        {
            get
            {
                int duration = 0;
                foreach (SpecSubject subject in this.Subjects)
                {
                    foreach (SpecContext context in subject.Contexts)
                    {
                        foreach (Spec spec in context.Specs)
                        {
                            duration += spec.DurationInMillis;
                        }
                    }
                }

                return duration;
            }
        }

        public bool AllPerfect
        {
            get
            {
                foreach (SpecSubject subject in this.Subjects)
                {
                    foreach (SpecContext context in subject.Contexts)
                    {
                        foreach (Spec spec in context.Specs)
                        {
                            if (spec.Status != ESpecStatus.Passed) return false;
                        }
                    }
                }

                return true;
            }
        }
    }

    public class SpecRun
    {
        public string Date { get; private set; }
        public int DurationInMillis { get; private set; }
        public IEnumerable<AssemblySpecs> Assemblies { get; private set; }

        public int SpecCount { get; private set; }
        public int SpecPassed { get; private set; }
        public int SpecIgnored { get; private set; }
        public int SpecNotImplemented { get; private set; }
        public int SpecFailed { get; private set; }

        public SpecRun(string date, int durationInMillis, IEnumerable<AssemblySpecs> assemblies)
        {
            this.Date = date;
            this.DurationInMillis = durationInMillis;
            this.Assemblies = assemblies;
            this.CalculateStatistics();
        }

        private void CalculateStatistics()
        {
            int specCount = 0;
            int specPassed = 0;
            int specIgnored = 0;
            int specNotImplemented = 0;
            int specFailed = 0;

            foreach (AssemblySpecs assembly in this.Assemblies)
            {
                foreach (SpecSubject subject in assembly.Subjects)
                {
                    foreach (SpecContext context in subject.Contexts)
                    {
                        foreach (Spec spec in context.Specs)
                        {
                            specCount++;
                            if (spec.Status == ESpecStatus.Passed) specPassed++;
                            if (spec.Status == ESpecStatus.Ignored) specIgnored++;
                            if (spec.Status == ESpecStatus.NotImplemented) specNotImplemented++;
                            if (spec.Status == ESpecStatus.Failed) specFailed++;
                        }
                    }
                }
            }

            this.SpecCount = specCount;
            this.SpecPassed = specPassed;
            this.SpecIgnored = specIgnored;
            this.SpecNotImplemented = specNotImplemented;
            this.SpecFailed = specFailed;
        }

    }

}
